1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package com.sun.media.sound;
26
27 import java.io.IOException;
28 import java.io.InputStream;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31
32 import javax.sound.sampled.AudioFormat;
33 import javax.sound.sampled.AudioInputStream;
34 import javax.sound.sampled.AudioSystem;
35 import javax.sound.sampled.AudioFormat.Encoding;
36 import javax.sound.sampled.spi.FormatConversionProvider;
37
38
39
40
41
42
43
44
45 public class AudioFloatFormatConverter extends FormatConversionProvider {
46
47 private static class AudioFloatFormatConverterInputStream extends
48 InputStream {
49 private AudioFloatConverter converter;
50
51 private AudioFloatInputStream stream;
52
53 private float[] readfloatbuffer;
54
55 private int fsize = 0;
56
57 public AudioFloatFormatConverterInputStream(AudioFormat targetFormat,
58 AudioFloatInputStream stream) {
59 this.stream = stream;
60 converter = AudioFloatConverter.getConverter(targetFormat);
61 fsize = ((targetFormat.getSampleSizeInBits() + 7) / 8);
62 }
63
64 public int read() throws IOException {
65 byte[] b = new byte[1];
66 int ret = read(b);
67 if (ret < 0)
68 return ret;
69 return b[0] & 0xFF;
70 }
71
72 public int read(byte[] b, int off, int len) throws IOException {
73
74 int flen = len / fsize;
75 if (readfloatbuffer == null || readfloatbuffer.length < flen)
76 readfloatbuffer = new float[flen];
77 int ret = stream.read(readfloatbuffer, 0, flen);
78 if (ret < 0)
79 return ret;
80 converter.toByteArray(readfloatbuffer, 0, ret, b, off);
81 return ret * fsize;
82 }
83
84 public int available() throws IOException {
85 int ret = stream.available();
86 if (ret < 0)
87 return ret;
88 return ret * fsize;
89 }
90
91 public void close() throws IOException {
92 stream.close();
93 }
94
95 public synchronized void mark(int readlimit) {
96 stream.mark(readlimit * fsize);
97 }
98
99 public boolean markSupported() {
100 return stream.markSupported();
101 }
102
103 public synchronized void reset() throws IOException {
104 stream.reset();
105 }
106
107 public long skip(long n) throws IOException {
108 long ret = stream.skip(n / fsize);
109 if (ret < 0)
110 return ret;
111 return ret * fsize;
112 }
113
114 }
115
116 private static class AudioFloatInputStreamChannelMixer extends
117 AudioFloatInputStream {
118
119 private int targetChannels;
120
121 private int sourceChannels;
122
123 private AudioFloatInputStream ais;
124
125 private AudioFormat targetFormat;
126
127 private float[] conversion_buffer;
128
129 public AudioFloatInputStreamChannelMixer(AudioFloatInputStream ais,
130 int targetChannels) {
131 this.sourceChannels = ais.getFormat().getChannels();
132 this.targetChannels = targetChannels;
133 this.ais = ais;
134 AudioFormat format = ais.getFormat();
135 targetFormat = new AudioFormat(format.getEncoding(), format
136 .getSampleRate(), format.getSampleSizeInBits(),
137 targetChannels, (format.getFrameSize() / sourceChannels)
138 * targetChannels, format.getFrameRate(), format
139 .isBigEndian());
140 }
141
142 public int available() throws IOException {
143 return (ais.available() / sourceChannels) * targetChannels;
144 }
145
146 public void close() throws IOException {
147 ais.close();
148 }
149
150 public AudioFormat getFormat() {
151 return targetFormat;
152 }
153
154 public long getFrameLength() {
155 return ais.getFrameLength();
156 }
157
158 public void mark(int readlimit) {
159 ais.mark((readlimit / targetChannels) * sourceChannels);
160 }
161
162 public boolean markSupported() {
163 return ais.markSupported();
164 }
165
166 public int read(float[] b, int off, int len) throws IOException {
167 int len2 = (len / targetChannels) * sourceChannels;
168 if (conversion_buffer == null || conversion_buffer.length < len2)
169 conversion_buffer = new float[len2];
170 int ret = ais.read(conversion_buffer, 0, len2);
171 if (ret < 0)
172 return ret;
173 if (sourceChannels == 1) {
174 int cs = targetChannels;
175 for (int c = 0; c < targetChannels; c++) {
176 for (int i = 0, ix = off + c; i < len2; i++, ix += cs) {
177 b[ix] = conversion_buffer[i];
178 }
179 }
180 } else if (targetChannels == 1) {
181 int cs = sourceChannels;
182 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
183 b[ix] = conversion_buffer[i];
184 }
185 for (int c = 1; c < sourceChannels; c++) {
186 for (int i = c, ix = off; i < len2; i += cs, ix++) {
187 b[ix] += conversion_buffer[i];
188 }
189 }
190 float vol = 1f / ((float) sourceChannels);
191 for (int i = 0, ix = off; i < len2; i += cs, ix++) {
192 b[ix] *= vol;
193 }
194 } else {
195 int minChannels = Math.min(sourceChannels, targetChannels);
196 int off_len = off + len;
197 int ct = targetChannels;
198 int cs = sourceChannels;
199 for (int c = 0; c < minChannels; c++) {
200 for (int i = off + c, ix = c; i < off_len; i += ct, ix += cs) {
201 b[i] = conversion_buffer[ix];
202 }
203 }
204 for (int c = minChannels; c < targetChannels; c++) {
205 for (int i = off + c; i < off_len; i += ct) {
206 b[i] = 0;
207 }
208 }
209 }
210 return (ret / sourceChannels) * targetChannels;
211 }
212
213 public void reset() throws IOException {
214 ais.reset();
215 }
216
217 public long skip(long len) throws IOException {
218 long ret = ais.skip((len / targetChannels) * sourceChannels);
219 if (ret < 0)
220 return ret;
221 return (ret / sourceChannels) * targetChannels;
222 }
223
224 }
225
226 private static class AudioFloatInputStreamResampler extends
227 AudioFloatInputStream {
228
229 private AudioFloatInputStream ais;
230
231 private AudioFormat targetFormat;
232
233 private float[] skipbuffer;
234
235 private SoftAbstractResampler resampler;
236
237 private float[] pitch = new float[1];
238
239 private float[] ibuffer2;
240
241 private float[][] ibuffer;
242
243 private float ibuffer_index = 0;
244
245 private int ibuffer_len = 0;
246
247 private int nrofchannels = 0;
248
249 private float[][] cbuffer;
250
251 private int buffer_len = 512;
252
253 private int pad;
254
255 private int pad2;
256
257 private float[] ix = new float[1];
258
259 private int[] ox = new int[1];
260
261 private float[][] mark_ibuffer = null;
262
263 private float mark_ibuffer_index = 0;
264
265 private int mark_ibuffer_len = 0;
266
267 public AudioFloatInputStreamResampler(AudioFloatInputStream ais,
268 AudioFormat format) {
269 this.ais = ais;
270 AudioFormat sourceFormat = ais.getFormat();
271 targetFormat = new AudioFormat(sourceFormat.getEncoding(), format
272 .getSampleRate(), sourceFormat.getSampleSizeInBits(),
273 sourceFormat.getChannels(), sourceFormat.getFrameSize(),
274 format.getSampleRate(), sourceFormat.isBigEndian());
275 nrofchannels = targetFormat.getChannels();
276 Object interpolation = format.getProperty("interpolation");
277 if (interpolation != null && (interpolation instanceof String)) {
278 String resamplerType = (String) interpolation;
279 if (resamplerType.equalsIgnoreCase("point"))
280 this.resampler = new SoftPointResampler();
281 if (resamplerType.equalsIgnoreCase("linear"))
282 this.resampler = new SoftLinearResampler2();
283 if (resamplerType.equalsIgnoreCase("linear1"))
284 this.resampler = new SoftLinearResampler();
285 if (resamplerType.equalsIgnoreCase("linear2"))
286 this.resampler = new SoftLinearResampler2();
287 if (resamplerType.equalsIgnoreCase("cubic"))
288 this.resampler = new SoftCubicResampler();
289 if (resamplerType.equalsIgnoreCase("lanczos"))
290 this.resampler = new SoftLanczosResampler();
291 if (resamplerType.equalsIgnoreCase("sinc"))
292 this.resampler = new SoftSincResampler();
293 }
294 if (resampler == null)
295 resampler = new SoftLinearResampler2();
296
297 pitch[0] = sourceFormat.getSampleRate() / format.getSampleRate();
298 pad = resampler.getPadding();
299 pad2 = pad * 2;
300 ibuffer = new float[nrofchannels][buffer_len + pad2];
301 ibuffer2 = new float[nrofchannels * buffer_len];
302 ibuffer_index = buffer_len + pad;
303 ibuffer_len = buffer_len;
304 }
305
306 public int available() throws IOException {
307 return 0;
308 }
309
310 public void close() throws IOException {
311 ais.close();
312 }
313
314 public AudioFormat getFormat() {
315 return targetFormat;
316 }
317
318 public long getFrameLength() {
319 return AudioSystem.NOT_SPECIFIED;
320 }
321
322 public void mark(int readlimit) {
323 ais.mark((int) (readlimit * pitch[0]));
324 mark_ibuffer_index = ibuffer_index;
325 mark_ibuffer_len = ibuffer_len;
326 if (mark_ibuffer == null) {
327 mark_ibuffer = new float[ibuffer.length][ibuffer[0].length];
328 }
329 for (int c = 0; c < ibuffer.length; c++) {
330 float[] from = ibuffer[c];
331 float[] to = mark_ibuffer[c];
332 for (int i = 0; i < to.length; i++) {
333 to[i] = from[i];
334 }
335 }
336 }
337
338 public boolean markSupported() {
339 return ais.markSupported();
340 }
341
342 private void readNextBuffer() throws IOException {
343
344 if (ibuffer_len == -1)
345 return;
346
347 for (int c = 0; c < nrofchannels; c++) {
348 float[] buff = ibuffer[c];
349 int buffer_len_pad = ibuffer_len + pad2;
350 for (int i = ibuffer_len, ix = 0; i < buffer_len_pad; i++, ix++) {
351 buff[ix] = buff[i];
352 }
353 }
354
355 ibuffer_index -= (ibuffer_len);
356
357 ibuffer_len = ais.read(ibuffer2);
358 if (ibuffer_len >= 0) {
359 while (ibuffer_len < ibuffer2.length) {
360 int ret = ais.read(ibuffer2, ibuffer_len, ibuffer2.length
361 - ibuffer_len);
362 if (ret == -1)
363 break;
364 ibuffer_len += ret;
365 }
366 Arrays.fill(ibuffer2, ibuffer_len, ibuffer2.length, 0);
367 ibuffer_len /= nrofchannels;
368 } else {
369 Arrays.fill(ibuffer2, 0, ibuffer2.length, 0);
370 }
371
372 int ibuffer2_len = ibuffer2.length;
373 for (int c = 0; c < nrofchannels; c++) {
374 float[] buff = ibuffer[c];
375 for (int i = c, ix = pad2; i < ibuffer2_len; i += nrofchannels, ix++) {
376 buff[ix] = ibuffer2[i];
377 }
378 }
379
380 }
381
382 public int read(float[] b, int off, int len) throws IOException {
383
384 if (cbuffer == null || cbuffer[0].length < len / nrofchannels) {
385 cbuffer = new float[nrofchannels][len / nrofchannels];
386 }
387 if (ibuffer_len == -1)
388 return -1;
389 if (len < 0)
390 return 0;
391 int offlen = off + len;
392 int remain = len / nrofchannels;
393 int destPos = 0;
394 int in_end = ibuffer_len;
395 while (remain > 0) {
396 if (ibuffer_len >= 0) {
397 if (ibuffer_index >= (ibuffer_len + pad))
398 readNextBuffer();
399 in_end = ibuffer_len + pad;
400 }
401
402 if (ibuffer_len < 0) {
403 in_end = pad2;
404 if (ibuffer_index >= in_end)
405 break;
406 }
407
408 if (ibuffer_index < 0)
409 break;
410 int preDestPos = destPos;
411 for (int c = 0; c < nrofchannels; c++) {
412 ix[0] = ibuffer_index;
413 ox[0] = destPos;
414 float[] buff = ibuffer[c];
415 resampler.interpolate(buff, ix, in_end, pitch, 0,
416 cbuffer[c], ox, len / nrofchannels);
417 }
418 ibuffer_index = ix[0];
419 destPos = ox[0];
420 remain -= destPos - preDestPos;
421 }
422 for (int c = 0; c < nrofchannels; c++) {
423 int ix = 0;
424 float[] buff = cbuffer[c];
425 for (int i = c + off; i < offlen; i += nrofchannels) {
426 b[i] = buff[ix++];
427 }
428 }
429 return len - remain * nrofchannels;
430 }
431
432 public void reset() throws IOException {
433 ais.reset();
434 if (mark_ibuffer == null)
435 return;
436 ibuffer_index = mark_ibuffer_index;
437 ibuffer_len = mark_ibuffer_len;
438 for (int c = 0; c < ibuffer.length; c++) {
439 float[] from = mark_ibuffer[c];
440 float[] to = ibuffer[c];
441 for (int i = 0; i < to.length; i++) {
442 to[i] = from[i];
443 }
444 }
445
446 }
447
448 public long skip(long len) throws IOException {
449 if (len < 0)
450 return 0;
451 if (skipbuffer == null)
452 skipbuffer = new float[1024 * targetFormat.getFrameSize()];
453 float[] l_skipbuffer = skipbuffer;
454 long remain = len;
455 while (remain > 0) {
456 int ret = read(l_skipbuffer, 0, (int) Math.min(remain,
457 skipbuffer.length));
458 if (ret < 0) {
459 if (remain == len)
460 return ret;
461 break;
462 }
463 remain -= ret;
464 }
465 return len - remain;
466
467 }
468
469 }
470
471 private Encoding[] formats = { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
472 Encoding.PCM_FLOAT };
473
474 public AudioInputStream getAudioInputStream(Encoding targetEncoding,
475 AudioInputStream sourceStream) {
476 if (sourceStream.getFormat().getEncoding().equals(targetEncoding))
477 return sourceStream;
478 AudioFormat format = sourceStream.getFormat();
479 int channels = format.getChannels();
480 Encoding encoding = targetEncoding;
481 float samplerate = format.getSampleRate();
482 int bits = format.getSampleSizeInBits();
483 boolean bigendian = format.isBigEndian();
484 if (targetEncoding.equals(Encoding.PCM_FLOAT))
485 bits = 32;
486 AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits,
487 channels, channels * bits / 8, samplerate, bigendian);
488 return getAudioInputStream(targetFormat, sourceStream);
489 }
490
491 public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
492 AudioInputStream sourceStream) {
493 if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
494 throw new IllegalArgumentException("Unsupported conversion: "
495 + sourceStream.getFormat().toString() + " to "
496 + targetFormat.toString());
497 return getAudioInputStream(targetFormat, AudioFloatInputStream
498 .getInputStream(sourceStream));
499 }
500
501 public AudioInputStream getAudioInputStream(AudioFormat targetFormat,
502 AudioFloatInputStream sourceStream) {
503
504 if (!isConversionSupported(targetFormat, sourceStream.getFormat()))
505 throw new IllegalArgumentException("Unsupported conversion: "
506 + sourceStream.getFormat().toString() + " to "
507 + targetFormat.toString());
508 if (targetFormat.getChannels() != sourceStream.getFormat()
509 .getChannels())
510 sourceStream = new AudioFloatInputStreamChannelMixer(sourceStream,
511 targetFormat.getChannels());
512 if (Math.abs(targetFormat.getSampleRate()
513 - sourceStream.getFormat().getSampleRate()) > 0.000001)
514 sourceStream = new AudioFloatInputStreamResampler(sourceStream,
515 targetFormat);
516 return new AudioInputStream(new AudioFloatFormatConverterInputStream(
517 targetFormat, sourceStream), targetFormat, sourceStream
518 .getFrameLength());
519 }
520
521 public Encoding[] getSourceEncodings() {
522 return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
523 Encoding.PCM_FLOAT };
524 }
525
526 public Encoding[] getTargetEncodings() {
527 return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
528 Encoding.PCM_FLOAT };
529 }
530
531 public Encoding[] getTargetEncodings(AudioFormat sourceFormat) {
532 if (AudioFloatConverter.getConverter(sourceFormat) == null)
533 return new Encoding[0];
534 return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED,
535 Encoding.PCM_FLOAT };
536 }
537
538 public AudioFormat[] getTargetFormats(Encoding targetEncoding,
539 AudioFormat sourceFormat) {
540 if (AudioFloatConverter.getConverter(sourceFormat) == null)
541 return new AudioFormat[0];
542 int channels = sourceFormat.getChannels();
543
544 ArrayList<AudioFormat> formats = new ArrayList<AudioFormat>();
545
546 if (targetEncoding.equals(Encoding.PCM_SIGNED))
547 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
548 AudioSystem.NOT_SPECIFIED, 8, channels, channels,
549 AudioSystem.NOT_SPECIFIED, false));
550 if (targetEncoding.equals(Encoding.PCM_UNSIGNED))
551 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
552 AudioSystem.NOT_SPECIFIED, 8, channels, channels,
553 AudioSystem.NOT_SPECIFIED, false));
554
555 for (int bits = 16; bits < 32; bits += 8) {
556 if (targetEncoding.equals(Encoding.PCM_SIGNED)) {
557 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
558 AudioSystem.NOT_SPECIFIED, bits, channels, channels
559 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
560 formats.add(new AudioFormat(Encoding.PCM_SIGNED,
561 AudioSystem.NOT_SPECIFIED, bits, channels, channels
562 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
563 }
564 if (targetEncoding.equals(Encoding.PCM_UNSIGNED)) {
565 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
566 AudioSystem.NOT_SPECIFIED, bits, channels, channels
567 * bits / 8, AudioSystem.NOT_SPECIFIED, true));
568 formats.add(new AudioFormat(Encoding.PCM_UNSIGNED,
569 AudioSystem.NOT_SPECIFIED, bits, channels, channels
570 * bits / 8, AudioSystem.NOT_SPECIFIED, false));
571 }
572 }
573
574 if (targetEncoding.equals(Encoding.PCM_FLOAT)) {
575 formats.add(new AudioFormat(Encoding.PCM_FLOAT,
576 AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
577 AudioSystem.NOT_SPECIFIED, false));
578 formats.add(new AudioFormat(Encoding.PCM_FLOAT,
579 AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4,
580 AudioSystem.NOT_SPECIFIED, true));
581 formats.add(new AudioFormat(Encoding.PCM_FLOAT,
582 AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
583 AudioSystem.NOT_SPECIFIED, false));
584 formats.add(new AudioFormat(Encoding.PCM_FLOAT,
585 AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8,
586 AudioSystem.NOT_SPECIFIED, true));
587 }
588
589 return formats.toArray(new AudioFormat[formats.size()]);
590 }
591
592 public boolean isConversionSupported(AudioFormat targetFormat,
593 AudioFormat sourceFormat) {
594 if (AudioFloatConverter.getConverter(sourceFormat) == null)
595 return false;
596 if (AudioFloatConverter.getConverter(targetFormat) == null)
597 return false;
598 if (sourceFormat.getChannels() <= 0)
599 return false;
600 if (targetFormat.getChannels() <= 0)
601 return false;
602 return true;
603 }
604
605 public boolean isConversionSupported(Encoding targetEncoding,
606 AudioFormat sourceFormat) {
607 if (AudioFloatConverter.getConverter(sourceFormat) == null)
608 return false;
609 for (int i = 0; i < formats.length; i++) {
610 if (targetEncoding.equals(formats[i]))
611 return true;
612 }
613 return false;
614 }
615
616 }